/**
*     Copyright (c) 2023, Nations Technologies Inc.
* 
*     All rights reserved.
*
*     This software is the exclusive property of Nations Technologies Inc. (Hereinafter 
* referred to as NATIONS). This software, and the product of NATIONS described herein 
* (Hereinafter referred to as the Product) are owned by NATIONS under the laws and treaties
* of the People's Republic of China and other applicable jurisdictions worldwide.
*
*     NATIONS does not grant any license under its patents, copyrights, trademarks, or other 
* intellectual property rights. Names and brands of third party may be mentioned or referred 
* thereto (if any) for identification purposes only.
*
*     NATIONS reserves the right to make changes, corrections, enhancements, modifications, and 
* improvements to this software at any time without notice. Please contact NATIONS and obtain 
* the latest version of this software before placing orders.

*     Although NATIONS has attempted to provide accurate and reliable information, NATIONS assumes 
* no responsibility for the accuracy and reliability of this software.
* 
*     It is the responsibility of the user of this software to properly design, program, and test 
* the functionality and safety of any application made of this information and any resulting product. 
* In no event shall NATIONS be liable for any direct, indirect, incidental, special,exemplary, or 
* consequential damages arising in any way out of the use of this software or the Product.
*
*     NATIONS Products are neither intended nor warranted for usage in systems or equipment, any
* malfunction or failure of which may cause loss of human life, bodily injury or severe property 
* damage. Such applications are deemed, "Insecure Usage".
*
*     All Insecure Usage shall be made at user's risk. User shall indemnify NATIONS and hold NATIONS 
* harmless from and against all claims, costs, damages, and other liabilities, arising from or related 
* to any customer's Insecure Usage.

*     Any express or implied warranty with regard to this software or the Product, including,but not 
* limited to, the warranties of merchantability, fitness for a particular purpose and non-infringement
* are disclaimed to the fullest extent permitted by law.

*     Unless otherwise explicitly permitted by NATIONS, anyone may not duplicate, modify, transcribe
* or otherwise distribute this software for any purposes, in whole or in part.
*
*     NATIONS products and technologies shall not be used for or incorporated into any products or systems
* whose manufacture, use, or sale is prohibited under any applicable domestic or foreign laws or regulations. 
* User shall comply with any applicable export control laws and regulations promulgated and administered by 
* the governments of any countries asserting jurisdiction over the parties or transactions.
**/


/**
*\*\file      spi_flash.c
*\*\author    Nations
*\*\version   v1.0.0
*\*\copyright Copyright (c) 2023, Nations Technologies Inc. All rights reserved. 
*/
#include    "n32h47x_48x_xspi.h"
#include    "NandFlash.h"
#include    <stdio.h>
#include    <string.h>
#include    "xspi_cfg.h"

/** User application include */

/**
*\*\name    XSPI_Configuration.
*\*\fun     Configures the xspi register.
*\*\param  frame_format:
*\*\        -STANDRD_SPI_FORMAT
*\*\        -DUAL_SPI_FORMAT
*\*\        -QUAD_SPI_FORMAT
*\*\        -OCTAL_SPI_FORMAT
*\*\param  transfer_mode:
*\*\        -TX_AND_RX
*\*\        -TX_ONLY
*\*\        -RX_ONLY
*\*\        -EEPROM_READ
*\*\param  data_frame_size:
*\*\        -DFS_04_BIT
*\*\        -DFS_08_BIT
*\*\        -DFS_16_BIT
*\*\        -DFS_24_BIT
*\*\        -DFS_32_BIT
*\*\param  number_data_frame:
*\*\        -0-0xffff
*\*\param  baudr:
*\*\        -0-0x7fff
*\*\return  none
*\*\note default:INST_8BIT,ADDR_28BIT,waitcycle_8
**/
static void BSP_xSPIConfiguration(uint32_t frame_format,uint32_t transfer_mode,uint32_t data_frame_size,uint16_t number_data_frame,uint16_t baudr)
{
    XSPI_DeInit();
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_XSPI, ENABLE);
    /*disable XSPI*/
    XSPI->SLAVE_EN = (0x0);
    XSPI_Cmd(DISABLE);
    
    /*set MST, SPI_FRF, TMOD ,SCPOL, SCPH, DFS, SSTE*/
    XSPI->CTRL0 = (XSPI_Mode_Master|frame_format|transfer_mode|SCPOL_LOW|START_BIT|data_frame_size|SSTE_BIT0);
    /*set NDF*/
    XSPI->CTRL1 = (number_data_frame);
    /*set CLKDIV */
    XSPI->BAUD = ((uint32_t)baudr<<1);
    
    /*set TRANSTYPE, ADDRLEN, INSTL, WAITCYCLES*/
    XSPI->ENH_CTRL0 = (TRANS_TYPE_STANDRD|ENSPI_ADDR_16BIT|ENSPI_INST_L_08BIT|ENSPI_WAIT_8CYCLES);
    if(transfer_mode == TX_ONLY || transfer_mode == RX_ONLY)
    {
        XSPI->RX_DELAY = (XSPI->BAUD)/2;
        XSPI->ENH_CTRL0 |=CLK_STRETCH_EN;
    }
    /*set TXFTTEI*/
    XSPI->TXFT = ((0x00<<16)|0x00)&0x1f001f;/* level = 0*/
    /*set TXFTTEI*/
    XSPI->RXFT = (0x08&0x1f);/* level = 8*/
    
    /*enable XSPI*/
    XSPI->SLAVE_EN = (0x1);
    XSPI_Cmd(ENABLE);
}


uint8_t XSPI_FlashREG_RD(uint8_t cmd, uint8_t *pParam, uint8_t pLen, uint8_t *pOut, uint16_t rxLen)
{
    uint32_t rxCnt = 0;

    while (XSPI->STS&XSPI_FLAG_RFNE) { /*if RX FIFO Not Empty, clear FIFO*/
        *pOut = XSPI->DAT0;
    }
    if(XSPI->STS&XSPI_FLAG_TFNF) { /*wait tx FIFO not full flag set*/
        XSPI->DAT0 = cmd;
    }
    uint8_t txCnt = pLen + 1;
    while (pLen--) {
        if(XSPI->STS&XSPI_FLAG_TFNF) { /*wait tx FIFO not full flag set*/
            if (!pParam) {
                XSPI->DAT0 = DUMMY;    /* Sent the dummy byte*/
            } else {
                XSPI->DAT0 = *pParam++;
            }
            if(XSPI->STS&XSPI_FLAG_RFNE) {/*wait RX FIFO Not Empty flag set*/
                *pOut = XSPI->DAT0;
                rxCnt++;
            }
        }
    }
    if (!rxLen) rxLen = 1;
    if (!pOut) return 0;
    while (rxLen--) {
        if(XSPI->STS&XSPI_FLAG_TFNF) { /*wait tx FIFO not full flag set*/
            XSPI->DAT0 = 0xFF;
            if(XSPI->STS&XSPI_FLAG_RFNE) {/*wait RX FIFO Not Empty flag set*/
                rxCnt++;
                if (rxCnt > txCnt) {
                    *pOut++ = XSPI->DAT0;
                } else {
                    (void)XSPI->DAT0;
                }
            }
        }
    }

    uint32_t wtime = 1000;  // Setup a max watting time
    while(((XSPI->STS&XSPI_FLAG_TFE)==RESET)&&wtime)  /*wait tx FIFO Empty*/
    {wtime--;}

    do {
        if(XSPI->STS&XSPI_FLAG_RFNE) { /*wait RX FIFO Not Empty flag set*/
            rxCnt++;
            if (rxCnt > txCnt) {
                *pOut++ = XSPI->DAT0;
            } else {
                (void)XSPI->DAT0;
            }
        }
    }
    while((XSPI->STS&XSPI_FLAG_BUSY)||(XSPI->STS&XSPI_FLAG_RFNE));/*wait XSPI idle*/
//    SYS_LOGI("<SPI_FLASH> rxCnt<%d>,txLen<%d>,txAll<%d>\r\n", rxCnt, txCnt, txAll);
    
    return (1);
}

uint8_t XSPI_FlashREG_WR(uint8_t cmd, uint8_t *pParam, uint8_t pLen)
{
    if(XSPI->STS&XSPI_FLAG_TFNF) { /*wait tx FIFO not full flag set*/
        XSPI->DAT0 = cmd;
    }
    if (!pParam) pLen = 0;
    if (pLen) {
        while (pLen--) {
            if(XSPI->STS&XSPI_FLAG_TFNF) { /*wait tx FIFO not full flag set*/
                XSPI->DAT0 = *pParam++;
            }
        }
    } else {
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
    }
    uint32_t wtime = 1000;  // Setup a max watting time
    while(((XSPI->STS&XSPI_FLAG_TFE)==RESET)&&wtime) /*wait tx FIFO Empty*/
    {wtime--;}

    while(XSPI->STS&XSPI_FLAG_BUSY);

    return 1;
}

void XSPI_FlashWrite( uint32_t PrgCmd,uint32_t addr, uint16_t count,uint32_t *Wbuf)
{
    uint32_t num=0;

    XSPI->DAT0 = (PrgCmd);
    XSPI->DAT0 = (addr);
    num = 0;
    while(num<count)
    {
        if((XSPI->STS&XSPI_FLAG_TFNF)==XSPI_FLAG_TFNF)/*TX FIFO not Full flag set */
        {
            XSPI->DAT0 = (Wbuf[num++]);
        }
    }
    while((XSPI->STS&XSPI_FLAG_TFE)==RESET);/*tx FIFO Empty flag set*/
    while(XSPI->STS&XSPI_FLAG_BUSY);/*wait xspi not busy*/
    
}

void XSPI_FlashWriteByte( uint32_t PrgCmd,uint32_t addr, uint16_t count,uint8_t *Wbuf)
{
    uint32_t num=0;

    XSPI->DAT0 = (PrgCmd);
    XSPI->DAT0 = (addr);
    num = 0;
    while(num<count) {
        if((XSPI->STS&XSPI_FLAG_TFNF)==XSPI_FLAG_TFNF)/*TX FIFO not Full flag set */
        {
            XSPI->DAT0 = (Wbuf[num++]);
        }
    }
    while((XSPI->STS&XSPI_FLAG_TFE)==RESET);/*tx FIFO Empty flag set*/
    while(XSPI->STS&XSPI_FLAG_BUSY);/*wait xspi not busy*/
    
}

int XSPI_FlashRead(uint32_t PrgCmd, uint32_t  StrAddr, uint32_t Len, uint32_t *DstBuf)
{
    u32 num=0;
    XSPI->DAT0 = PrgCmd;
    XSPI->DAT0 = StrAddr;
    num=0;
    while(num<Len)
    {
        if((XSPI->STS & XSPI_FLAG_RFNE))/*RX FIFO not empty*/
        {
            DstBuf[num++] = XSPI->DAT0;
        }
    }
    return 0;
}

int XSPI_FlashReadByte(uint32_t PrgCmd, uint32_t  StrAddr, uint32_t Len, uint8_t *DstBuf)
{
    u32 num=0;
    XSPI->DAT0 = PrgCmd;
    XSPI->DAT0 = StrAddr;
    num=0;
    while(num<Len) {
        if((XSPI->STS & XSPI_FLAG_RFNE))/*RX FIFO not empty*/
        {
            DstBuf[num++] = XSPI->DAT0;
        }
    }
    return 0;
}

uint8_t SpiFlash_GetStatus(uint8_t tAddr)
{
    uint8_t retValue;
    XSPI_FlashREG_RD(SPIFLASH_READ_REG, &tAddr, 1, &retValue, 1);
    return retValue;
}

/* get the nand flash status */
uint8_t SpiFlash_Status(void)
{
    uint32_t deviceID = 0;
    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
    XSPI_FlashREG_RD(SPIFLASH_JEDECID, NULL, 1, (uint8_t *)&deviceID, 3);
    deviceID = ((deviceID>>24) | (deviceID<<24) | ((deviceID & 0xFF00)<<8) | 
            ((deviceID & 0xFF0000)>>8)) >> 8;
    // SYS_LOGI("FlashID 0x%06X\r\n", deviceID);
    if (deviceID != FLASH_DEVICEID) {
        return 0x01;
    } else {
        return 0;
    }
}

/* Enable the spi flash write
 * ret: return the enable status regValue
 */
uint8_t SpiFlash_WriteEnable(void)
{
    uint8_t newStatus = 0xFF;
    do {/*write enable*/
        XSPI_FlashREG_WR(SPIFLASH_Write_Enable, NULL, NULL);
        newStatus = SpiFlash_GetStatus(SPIFLASH_REG3ADDR);
    }while((newStatus & 0x03) != 0x02);
    return newStatus;
}

/* initial the nand flash*/
uint8_t SpiFlash_initialize(void)
{
    /*==========================Set WE===========================*/
    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
    SpiFlash_WriteEnable();
    uint8_t wParam[] = {SPIFLASH_REG1ADDR, 0};
    XSPI_FlashREG_WR(SPIFLASH_WRITE_REG, wParam, sizeof(wParam));    // Cancle the protect bit
    uint8_t status = SpiFlash_GetStatus(SPIFLASH_REG2ADDR);
    uint8_t newStatus = status & (~0x10);    // disable ECC
    uint8_t reg2[] = {SPIFLASH_REG2ADDR, newStatus};
    XSPI_FlashREG_WR(SPIFLASH_WRITE_REG, reg2, sizeof(reg2));    // disable ECC
    // SYS_LOGI("spi flash reg1Value 0x%02X\r\n", SpiFlash_GetStatus(SPIFLASH_REG1ADDR));
    // SYS_LOGI("spi flash reg2Value 0x%02X\r\n", SpiFlash_GetStatus(SPIFLASH_REG2ADDR));

    return SpiFlash_Status();
}

/* Spi flash execute the program
 * ret: return the enable status regValue
 */
uint8_t SpiFlash_PgmExecute(uint32_t pageAddr)
{
    uint8_t wParam[4];
    wParam[0] = (pageAddr & 0xFF000000) >> 24;
    wParam[1] = (pageAddr & 0xFF0000)   >> 16;
    wParam[2] = (pageAddr & 0xFF00)     >> 8;
    wParam[3] = (pageAddr & 0xFF);
    XSPI_FlashREG_WR(SPIFLASH_EXECUTE, &wParam[4-ADDR_BYTES], ADDR_BYTES);

    uint8_t newStatus = 0xFF;
    do {
        newStatus = SpiFlash_GetStatus(SPIFLASH_REG3ADDR);
    }while((newStatus & 0x01) != 0x00);

    return newStatus;
}

/* Spi flash reload the page data to the buffer
 * ret: return the enable status regValue
 */
uint8_t SpiFlash_PageDataReload(uint32_t pageAddr)
{
    uint8_t wParam[4];
    wParam[0] = (pageAddr & 0xFF000000) >> 24;
    wParam[1] = (pageAddr & 0xFF0000)   >> 16;
    wParam[2] = (pageAddr & 0xFF00)     >> 8;
    wParam[3] = (pageAddr & 0xFF);
    XSPI_FlashREG_WR(SPIFLASH_PAGEREAD, &wParam[4-ADDR_BYTES], ADDR_BYTES);

    uint8_t newStatus = 0xFF;
    do {
        newStatus = SpiFlash_GetStatus(SPIFLASH_REG3ADDR);
    }while((newStatus & 0x01) != 0x00);

    return newStatus;
}

/* Spi flash block erase, 128K
 * ret: return the enable status regValue
 */
uint8_t SpiFlash_BlockErase128K(uint32_t pageAddr)
{
    uint8_t newStatus = 0xFF;

    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
    SpiFlash_WriteEnable();
    uint8_t wParam[4];
    wParam[0] = (pageAddr & 0xFF000000) >> 24;
    wParam[1] = (pageAddr & 0xFF0000)   >> 16;
    wParam[2] = (pageAddr & 0xFF00)     >> 8;
    wParam[3] = (pageAddr & 0xFF);
    XSPI_FlashREG_WR(SPIFLASH_Block_Erase128KB, &wParam[4-ADDR_BYTES], ADDR_BYTES);

    do {
        newStatus = SpiFlash_GetStatus(SPIFLASH_REG3ADDR);
    }while((newStatus & 0x03) != 0x00);

    return newStatus;
}

uint8_t SpiFlash_QuadWrite(uint32_t pageAddr, uint16_t pageCount, uint8_t *Wbuf)
{
    uint32_t wdBuf[FLASH_PAGESIZE/4];
    uint8_t wStatus = 0;
    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);  // Config xSPI into standard mode
    for (uint32_t tAddr = pageAddr; tAddr < (pageAddr+pageCount); tAddr++) {
        SpiFlash_WriteEnable();
        BSP_xSPIConfiguration(QUAD_SPI_FORMAT,TX_ONLY,DFS_32_BIT, (FLASH_PAGESIZE/4)-1 ,3);
        memcpy(wdBuf, Wbuf, FLASH_PAGESIZE);
        Wbuf = Wbuf + FLASH_PAGESIZE;  // Point to next buffer;
        XSPI_FlashWrite(SPIFLASH_QuadPage_Pro, 0, (FLASH_PAGESIZE/4) ,wdBuf);
        BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
        wStatus = SpiFlash_PgmExecute(tAddr);
        
        if ((wStatus & 0x08) != 0) { // execute program error
            break;
        }
    }

    return wStatus;
}

uint8_t SpiFlash_QuadRead(uint32_t pageAddr, uint16_t pageCount, uint8_t *rBuf)
{
    uint32_t rdBuf[FLASH_PAGESIZE/4];
    uint8_t rStatus = 0;
    for (uint32_t tAddr = pageAddr; tAddr < (pageAddr+pageCount); tAddr++) {
        BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
        SpiFlash_PageDataReload(tAddr);
        BSP_xSPIConfiguration(QUAD_SPI_FORMAT,RX_ONLY,DFS_32_BIT, (FLASH_PAGESIZE/4)-1, 3);
        XSPI_FlashRead(SPIFLASH_Read_QUAD, 0, (FLASH_PAGESIZE/4) ,rdBuf);
        memcpy(rBuf, rdBuf, FLASH_PAGESIZE);
        rBuf = rBuf + FLASH_PAGESIZE;  // Point to next buffer;
    }
    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);

    return rStatus;
}

/*Must not open the ECC function*/
uint8_t SpiFlash_QuadWriteByte(uint32_t tAddr, uint16_t count, uint8_t *wBuf)
{
    uint32_t tPageStart = tAddr / FLASH_PAGESIZE;
    uint32_t tCAstart   = tAddr % FLASH_PAGESIZE;
    uint32_t tPageEnd   = (tAddr + count - 1) / FLASH_PAGESIZE;
    uint8_t wStatus = 0;

    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);  // Config xSPI into standard mode
    for (uint32_t exAddr = tPageStart; exAddr <= tPageEnd; exAddr++) {
        uint16_t num;
        if ((tCAstart + count) > FLASH_PAGESIZE) {
            num = FLASH_PAGESIZE - tCAstart;
            count = count - num;
        } else {
            num = count;
        }
        SpiFlash_WriteEnable();
        BSP_xSPIConfiguration(QUAD_SPI_FORMAT,TX_ONLY,DFS_08_BIT, num-1 ,3);
        XSPI_FlashWriteByte(SPIFLASH_QuadPage_Pro, tCAstart, num ,wBuf);
        BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
        wStatus = SpiFlash_PgmExecute(exAddr);
        wBuf = wBuf + num;
        tCAstart = 0;
        
        if ((wStatus & 0x08) != 0) { // execute program error
            break;
        }
    }

    return wStatus;
}

uint8_t SpiFlash_QuadReadByte(uint32_t tAddr, uint16_t count, uint8_t *rBuf)
{
    uint32_t tPageStart = tAddr / FLASH_PAGESIZE;
    uint32_t tCAstart   = tAddr % FLASH_PAGESIZE;
    uint32_t tPageEnd   = (tAddr + count - 1) / FLASH_PAGESIZE;
    uint8_t rStatus = 0;
    for (uint32_t exAddr = tPageStart; exAddr <= tPageEnd; exAddr++) {
        uint16_t num;
        if ((tCAstart + count) > FLASH_PAGESIZE) {
            num = FLASH_PAGESIZE - tCAstart;
            count = count - num;
        } else {
            num = count;
        }
        BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
        rStatus = SpiFlash_PageDataReload(exAddr);
        if ((rStatus & 0x30) != 0) { // execute program error
            break;
        }
        BSP_xSPIConfiguration(QUAD_SPI_FORMAT,RX_ONLY,DFS_08_BIT, num-1, 3);
        XSPI_FlashReadByte(SPIFLASH_Read_QUAD, tCAstart, num ,rBuf);
        rBuf = rBuf + num;  // Point to next buffer;
        tCAstart = 0;
    }
    BSP_xSPIConfiguration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);

    return rStatus;
}






